クラスメソッドメンバーズポータルのフロントエンドをリプレイスしたので技術スタックを紹介します
AWS事業本部サービス開発室の持田です。
普段はクラスメソッドメンバーズポータルのフロントエンド/バックエンドを開発・運用するお仕事をしています。
はじめに
弊社ではクラスメソッドメンバーズ(以下、メンバーズ)と呼ばれるAWSの利用費割引や請求代行、コンサルティング、セキュリティ、24/365サポートなどAWSに関することをおまかせしていただけるサービスを提供しています。
その中でメンバーズに契約いただいたお客様に対して、クラスメソッドメンバーズポータル(以下、CMP)というAWSアカウントリソースの利用状況を可視化できるWebサービスを提供しています。
今回CMPのフロントエンドを全面的をリプレイスしたので、その背景と採用している主な技術スタックについて紹介したいと思います。
リプレイスの背景
CMPは契約しているお客様向けに2013年頃から提供しているWebサービスになります。
リプレイス前のフロントエンド構成は2017年から稼働していますが、徐々に老朽化しており継続的なアップデートや機能追加が難しい状態になっていたため、全面的にリプレイスすることとなりました。
その他にもユーザーIDの管理方法変更に伴う認証基盤の刷新や、バックエンドAPI側のリプレイスも必要となったこともあり、結果的にサービスの大部分をリプレイスする大規模工事となりました。
リプレイスプロジェクトは2022年夏頃にスタートしましたが、2024年4月に無事GAすることができました。
採用した技術スタック
今回採用したフレームワーク・ライブラリを列挙します。
リプレイスプロジェクト自体は2022年夏頃にスタートしたため、技術選定についてはそのあたりの時期で検討・採用したものが多いです。
- フロントエンドフレームワーク
- UIフレームワーク
- フォームとバリデーション
- 状態管理とデータフェッチング
- 認証周り
- テスト
- 開発系
- インフラ(ホスティング)
構成の全体像
リプレイス後フロントエンド構成の全体像です。基本的にインフラはAWS上に構築しています。
フロントエンドのフレームワークとしてはNext.jsを採用しており、App Runner上で動作するように構築しています。アプリケーション用のコンテナイメージはECRで管理し、GitHubのリポジトリと連携するように構成しています。
フロントエンドではできるだけインフラ管理・運用のコストを抑えられるよう、ホスティング先としてApp Runnerを選択しました。
モニタリングツールとして一部CloudWatch RUMを利用しています。
また、IaCとしてはTerraformを利用しており、ほとんどのAWSリソースをTerraformで管理できるように定義しています。
フロントエンドフレームワーク
フロントエンドフレームワークはNext.js (Pages Router)を利用しています。
技術選定時の2022年夏頃はまだApp Routerが登場していなかったため、Pages Routerとなっています。
当時開発チームがメインで開発・運用しているシステムはVue.jsを利用していましたが、Reactを得意とするメンバーがいたことや後方互換性・継続的なメンテナンスが期待できる技術を採用したいという理由から、Reactベースとすることになりました。
また、リファレンスや公式のベストプラクティスが充実しておりフレームワーク側で実装の諸々を吸収できそうなことからNext.jsを採用しました。
(後方互換性的な話をすると、結果的にNext.jsはApp Routerが登場することになりましたが…😅)
実装上の設計として、ディレクトリ構成はBulletproof ReactのProject Structureをベースに、features
で機能ごとにコンポーネントを配置するようにしています。
src/
├── components # 画面やfeatureを問わず汎用的に使用するコンポーネント
├── features # 提供する機能に関するもの
├── hooks # 画面やfeatureを問わず汎用的に使用するHook
├── libs # 外部ライブラリのラッパー
├── locales # 言語ファイル等ロケールに関するもの
├── pages # 1画面を表すコンポーネント
├── providers # Providerを集約する
├── services # バックエンドAPIのクライアント実装
├── stores # グローバルステートに関するもの
├── styles # Styleに関するもの
├── tests # テストで使用するもの
└── utils # その他ユーティリティ
また、コンポーネントの実装においては下記のように単方向の依存を守るように気をつけています。
UIフレームワーク
UIフレームワークはCloudscapeを利用しています。
CloudscapeはAWSが作成しOSSとして公開しているデザインシステムです。主にAWSの製品やサービス(マネジメントコンソール)向けに構築され使用されています。
CMPはAWSを利用するお客様向けのサービスであり、AWSのサービスやマネジメントコンソールとの親和性が高そうなことからCloudscapeを採用しました。
Cloudscapeは比較的リッチなフレームワークなため、豊富なUIコンポーネントが用意されておりデザインの統一も容易なことによりエンジニア中心のメンバーでも実装しやすく、結果としてチームの開発効率も向上しました。
足りない部分は他のライブラリ等で補完する必要があったりするものの、アクセシビリティやレスポンシブデザインにも配慮されており使いやすいと感じました。
ライブラリ関連
フォームとバリデーション
フォーム作成にはReact Hook Formを、スキーマ定義およびバリデーションにはZodを利用しています。
Zodによるスキーマ定義は管理がしやすく、バリデーションのロジックをコンポーネントから切り離せるためいいですね。
状態管理とデータフェッチング
グローバルステート管理用にJotaiを利用しています。
管理が複雑化しないよう、基本的にグローバルステートの利用は最小限としています。
また、データフェッチングにはTanStack Queryを利用しています。APIを呼び出す場合は、直接コールせずにTanStack Query経由で可能な箇所はキャッシュするようにしています。
TanStack QueryはDevtoolsが用意されており、開発時にキャッシュの状態やクエリの実行状況をアプリケーション上で確認しやすいので便利です。
認証周り
認証機能のライブラリとしてAuth.js(実装当時はNextAuth.js)を利用しています。
新システムではClassmethod IDという独自のIDサービスと連携しますが、必要十分な機能が備わっておりシンプルで使いやすいです。
テスト
テスト周りのライブラリとしてはVitest、Storybook、Testing Libraryを組み合わせて利用しています。
実は最近までランナーとしてJestを利用していたのですが、mswやStorybookのメジャーアップデート時にテストがうまく動作しなくなる問題に遭遇してしまったため、思い切ってVitestに移行しました。
E2EテストにはPlaywrightを、Visual Regression Test(VRT)にはStorycapとreg-suitを組み合わせて利用しています。
基本UIに関する振る舞いテストやスナップショットテストはStorybookで行い、それ以外のユニットテストはVitestで行います。
ページ遷移や制御に関わるようなテストはPlaywrightでE2Eテストを行い、UIフレームワークのアップデートなどによる意図せぬ変更のチェック用にStorycap + reg-suitでVRTを行っています。
その他開発系
コンポーネントのScaffoldingにはscaffdogを利用しています。自動生成されるコンポーネントに最低限のテストコードを入れることで、実装者にTestとStorybookの実装を促すようにしています。
また、バックエンドAPIはOpen APIで定義されているため、APIクライアントと型の自動生成にOrvalを利用しています。
APIはそれなりにエンドポイントがあるのですが、Orvalを利用することで型安全なクライアントを自動生成できるため、新規実装時やAPI側の仕様が変更になった場合でも対応しやすくとても助かっています。
その他、Biome、lefthook、Renovateといった開発支援ツールも導入しています。
おわりに
リプレイスされたクラスメソッドメンバーズポータルのフロントエンドについて、技術スタックや採用しているライブラリなどを紹介しました。
一部、採用したフレームワークの重厚さに伴う課題はあったりするものの、チームとしては全体的に効率よく開発・運用できていると感じています。
現在、フロントエンド開発チームでは別の管理系システムでNext.js App Routerを採用した新規開発も進めているのですが、そちらも機会があれば紹介したいと思います。
(9/12追記)
弊社金谷が管理系システムの紹介記事を書いてくれたので、興味のある方はこちらもどうぞ!